
from typing import List
import x_com_lib
# import pandas as pd
import os
import csv
# from code_holder import CodeHolder
from code_holder import CodeHolder
from common import market_to_str
from converter import copy_min_data, subscribe_rt_data_to_topic
from rt_data.rt_data_holder import RTDataHolder
from x_com import xport_core
from x_com import source_api
from x_com.import_api import realtime_kdata_service_0_0_1
logger = xport_core.getLogger()


class RtdataSubscriberService:

    def __init__(self):
        self.csv_file_path = "D:/ex/temp"  # Path to save the CSV file
        self.csv_initialized = False

    async def on_init(self):
        self.data_path = x_com_lib.config.get_str("data-path")
        if self.data_path is None or self.data_path.strip() == "":
            raise Exception("data-path 不能为空！")
        # self.data_path = "C:/project"
        # 创建实时行情持有对象
        self.rt_data_holder = RTDataHolder()
        self.code_holder = CodeHolder()
        self.code_holder.load(self.data_path)

        logger.info("on_init...")

    async def on_finalize(self):
        logger.info("on_finalize...")

    # SubscribeRtData
    # 订阅实时行情

    async def subscribe_rt_data(self, ctx: xport_core.Context,  param: source_api.SubscribeRTDataRequest):
        subscibe_info = subscribe_rt_data_to_topic(param)
        x_com_lib.stream.dxc_subscribe_topic(subscibe_info)
        subscribe_param = realtime_kdata_service_0_0_1.SubscribeRTDataRequest()
        subscribe_param.market = param.market
        subscribe_param.code = param.code
        await realtime_kdata_service_0_0_1.subscribe_rt_data(subscribe_param)

    # CancelSubscribeRtData
    # 取消实时行情
    async def cancel_subscribe_rt_data(self, ctx: xport_core.Context,  param: source_api.SubscribeRTDataRequest):
        subscibe_info = subscribe_rt_data_to_topic(param)
        x_com_lib.stream.dxc_cancel_subscribe_topic(subscibe_info)
        subscribe_param = realtime_kdata_service_0_0_1.SubscribeRTDataRequest()
        subscribe_param.market = param.market
        subscribe_param.code = param.code
        await realtime_kdata_service_0_0_1.cancel_subscribe_rt_data(subscribe_param)
    # ClearMinData

    async def clear_min_data(self, ctx: xport_core.Context,  param: source_api.RTDataRequest):
        market_str = market_to_str(param.market)
        order_book_id = f"{market_str}.{param.code}"
        self.rt_data_holder.clear_min_data(order_book_id)


    def calc_average(self, min_datas:List[source_api.MinOrDayData]):
        turn_over = 0.0
        volume = 0.0
        #  计算的是分钟的均价线
        for min_data in min_datas:
            volume += min_data.volume
            turn_over += min_data.volume * min_data.close
            if volume != 0:
                min_data.average = turn_over / volume
    # GetRtMinData
    async def get_rt_min_data(self, ctx: xport_core.Context,  param: source_api.RTDataRequest) -> source_api.MinOrDayDataList:
        # 判断是否已经订阅，没有订阅的话，调用接口订阅
        market_str = market_to_str(param.market)
        order_book_id = f"{market_str}.{param.code}"
        min_datas = self.rt_data_holder.get_min_data(order_book_id)
        min_data_list = source_api.MinOrDayDataList()
        min_data_list.datas = min_datas
        # 当数据不为空，并且需要填充时，填充
        if not param.filled_up:
            # 计算的均价线
            self.calc_average( min_data_list.datas)
            return min_data_list

        # 数据为0 时，默认时间长度
        if len(min_data_list.datas) == 0:
            # 默认使用 20241105 因为录制的行情在 20241105 开始，真实情况，会使用当前交易日
            cur_day = 20241105
        else:
            # 根据实际行情，计算出真实的交易日
            cur_day = min_data_list.datas[0].datetime / 1_00_00_00_000
        # 获取交易时间点
        time_scope = self.code_holder.get_min_time_scope(order_book_id, int(cur_day), param.fre_num)
        # 如果交易时间点，为0 直接返回，这种情况基本不会发生，但是为了程序的严谨性，会增加判断
        if len(time_scope) == 0:
            return min_data_list
        
        cur_min_data_index = 0    
        # 填充好的分钟数据
        fillup_min_datas = []
        # 对齐行情
        i = 0
        data_len = len(min_data_list.datas)
        # 1 分钟频率
        if param.fre_num == 1:
          # 判断当前是否有数据, 没有的话,以时间轴第一个交易时间为当前时间
          if len(min_data_list.datas) == 0:
              cur_min_data = source_api.MinOrDayData()
              cur_min_data.datetime = time_scope[0]
          else:
              # 如果已经有时间, 则获取第一根 bar 的时间
              cur_min_data = min_data_list.datas[cur_min_data_index]
          while i < len(time_scope):
              cur_datetime = time_scope[i]
              #   
              if cur_min_data.datetime < cur_datetime and cur_min_data_index < data_len:
                  cur_min_data_index += 1
                  if cur_min_data_index < data_len:
                    cur_min_data = min_data_list.datas[cur_min_data_index]
                  continue
              # 等于当前的时间
              if cur_min_data.datetime == cur_datetime:
                  fillup_min_datas.append(cur_min_data),
                  # 分钟线移动到下一根
                  if cur_min_data_index < len(min_data_list.datas) - 1:
                      cur_min_data_index += 1
                      cur_min_data = min_data_list.datas[cur_min_data_index]
              # 大于当前时间，补齐
              elif cur_min_data.datetime > cur_datetime:
                  # 拿当前的时间进行补齐
                  new_min_data = copy_min_data(cur_min_data, cur_datetime)
                  fillup_min_datas.append(new_min_data)
              else:
                  # 后面的行情还没刷新，全部为0
                  new_min_data = source_api.MinOrDayData()
                  new_min_data.datetime = cur_datetime
                  fillup_min_datas.append(new_min_data)
              i += 1
          min_data_list.datas = fillup_min_datas
        else:
            # 填充好的分钟数据
            fillup_min_datas = []
            cur_min_data = source_api.MinOrDayData()
            cur_min_data.datetime = time_scope[0]
            fillup_min_datas.append(cur_min_data)
            if len(min_data_list.datas) != 0:
                first_bar_data = min_data_list.datas[0]
                #  只要价格,即可用于判断最高价最低价
                cur_min_data.open = first_bar_data.open
                cur_min_data.high = first_bar_data.high
                cur_min_data.low = first_bar_data.low
                cur_min_data.close = first_bar_data.close
                cur_min_data.settlement = 0
                cur_min_data.volume = 0
                cur_min_data.total_turnover = 0
                cur_min_data.open_interest = 0
            # 第一根数据的时间
            while i < len(time_scope):
                # 当前的时间点
                cur_datetime = time_scope[i]
                #  这一步, 当前的 数据没有刷新到这个时间点,数据全部填充为 0 即可
                if cur_min_data_index >= len(min_data_list.datas):
                    # 已经添加过了,同一个时间点不添加
                    if cur_min_data.datetime != cur_datetime:
                        cur_min_data = source_api.MinOrDayData()
                        cur_min_data.datetime = cur_datetime
                        fillup_min_datas.append(cur_min_data)
                    i += 1
                    continue
                # 
                # 如果当前的数据, 小于当前的时间点, 要合并到当前的时间里面
                if min_data_list.datas[cur_min_data_index].datetime < cur_datetime:
                    # 合并数据
                    if cur_min_data.high < min_data_list.datas[cur_min_data_index].high:
                        cur_min_data.high = min_data_list.datas[cur_min_data_index].high
                    if cur_min_data.low > min_data_list.datas[cur_min_data_index].low:
                        cur_min_data.low = min_data_list.datas[cur_min_data_index].low
                    cur_min_data.close = min_data_list.datas[cur_min_data_index].close
                    # 把这个时间段内的数据,都相加
                    cur_min_data.settlement +=  min_data_list.datas[cur_min_data_index].settlement
                    cur_min_data.volume +=  min_data_list.datas[cur_min_data_index].volume
                    cur_min_data.total_turnover +=  min_data_list.datas[cur_min_data_index].total_turnover
                    cur_min_data.open_interest +=  min_data_list.datas[cur_min_data_index].open_interest
                    #  移动到下一根数据
                    cur_min_data_index += 1
                    continue
                # 把数据存到数据
               
                # 切换到下一个频率的数据
                # 到结尾了,不添加
                i += 1
                if i >= len(time_scope):
                    continue
                cur_min_data = source_api.MinOrDayData()
                #  添加数据
                fillup_min_datas.append(cur_min_data)
                # 切换到下一个时间点
                cur_min_data.datetime = time_scope[i]
                cur_min_data.open = min_data_list.datas[cur_min_data_index].open
                cur_min_data.high = min_data_list.datas[cur_min_data_index].high
                cur_min_data.low = min_data_list.datas[cur_min_data_index].low
                cur_min_data.close = min_data_list.datas[cur_min_data_index].close
                cur_min_data.settlement = min_data_list.datas[cur_min_data_index].settlement
                cur_min_data.volume = min_data_list.datas[cur_min_data_index].volume
                cur_min_data.total_turnover = min_data_list.datas[cur_min_data_index].total_turnover
                cur_min_data.open_interest = min_data_list.datas[cur_min_data_index].open_interest
                cur_min_data_index += 1
                # 这里判断数据已经到最后一根了
                if cur_min_data_index >= len(min_data_list.datas):
                    fillup_min_datas.append(cur_min_data)
            min_data_list.datas = fillup_min_datas    
        # 计算的均价线
        self.calc_average(min_data_list.datas)
        return min_data_list

    # SearchCode
    async def search_code(self, ctx: xport_core.Context,  param: source_api.SearchCodeRequest) -> source_api.CodeInfoList:
        # 搜索代码
        code_list = source_api.CodeInfoList()
        for code_info in self.code_holder.code_dict.values():
            # 取前 50 即可
            if len(code_list.datas) >= 50:
                break
            if code_info.code.startswith(param.keyword) or code_info.name.startswith(param.keyword) or code_info.short_en.startswith(param.keyword):
                code_list.datas.append(code_info)
                continue

            if code_info.trading_code is not None and code_info.trading_code.startswith(param.keyword):
                code_list.datas.append(code_info)

        return code_list

  # FindData
    async def find_data(self, ctx: xport_core.Context,  param: source_api.FindDataRequest) -> source_api.FindDataResponse:
        resp = source_api.FindDataResponse()
        market_str = market_to_str(param.market)
        order_book_id = f"{market_str}.{param.code}"
        # 构建CSV文件的路径
        # 20241105
        file_path = f"{self.csv_file_path}/{order_book_id}_{param.date}.csv"
        # print(file_path)
        if not os.path.exists(file_path):
            logger.error(f"文件 {file_path} 未找到")
            return resp
        # 读取CSV文件
        # try:
        #     df = pd.read_csv(file_path)
        # except FileNotFoundError:
        #     logger.error(f"文件 {file_path} 未找到")
        #     return resp
        # except pd.errors.EmptyDataError:
        #     logger.error(f"文件 {file_path} 为空")
        #     return resp
        # except Exception as e:
        #     logger.error(f"读取文件 {file_path} 时发生错误: {e}")
        #     return resp
        min_or_day_data_list = []
        with open(file_path, mode='r', encoding='utf-8') as file:
            reader = csv.reader(file)
            # 跳过第一行（表头）
            next(reader)
            # 遍历CSV文件中的每一行
            for row in reader:
                # print(row)
                min_or_day_data = source_api.MinOrDayData()
                min_or_day_data.datetime = int(row[0])
                min_or_day_data.open = float(row[1])
                min_or_day_data.high = float(row[2])
                min_or_day_data.low = float(row[3])
                min_or_day_data.close = float(row[4])
                min_or_day_data.volume = float(row[5])

                min_or_day_data_list.append(min_or_day_data)
            
        # 根据提供的参数筛选数据
        # mask = (df['datetime'] == datetime) & \
        #        (df['open'] == open) & \
        #        (df['high'] == high) & \
        #        (df['low'] == low) & \
        #        (df['close'] == close) & \
        #        (df['volume'] == volume)
        
        # 获取筛选后的数据
        # filtered_df = df[mask]
        
        # 将筛选后的数据转换为 MinOrDayData 列表
        # min_or_day_data_list = []
  
        # for index, row in df.iterrows():
        #     print(row)
        #     try:
        #         print(row.open)
        #     except Exception as e:
        #         print(f"e = {e}")
        #     break
        #     min_or_day_data = source_api.MinOrDayData()
        #     # print(int(float(row['datetime'])))
        #     min_or_day_data.datetime =  int(float(row['datetime']))
        #     print(row['open'])
        #     # min_or_day_data.open = float(row['open'])
        #     # min_or_day_data.high = float(row['high'])
        #     # min_or_day_data.low = float(row['low'])
        #     # min_or_day_data.close = float(row['close'])
        #     # min_or_day_data.volume = float(row['volume'])
        #     # 其他字段也需要根据实际情况进行赋值
        #     min_or_day_data_list.append(min_or_day_data)
        self.calc_average(min_or_day_data_list)
        resp.data = min_or_day_data_list
        return resp


